package org.liaohailong.library.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;

import org.liaohailong.library.R;

/**
 * create by liaohailong
 * time 2023/3/7 19:21
 * desc: 左滑，显示右侧菜单
 * PS：
 * 1，仅有两个child，第一个为内容布局，第二个为菜单布局
 * 2，暂时只能在xml中使用
 * 3，不支持padding属性
 */
public class SlideLinearLayout extends FrameLayout {
    private static final String TAG = "SlideLinearLayout";

    private void log(@NonNull String msg, Object... args) {
        Log.i(TAG, String.format(msg, args));
    }

    private int touchScroll; // 通过这个距离值，来判断用户是否滑动了
    private View contentView;
    private View menuView;

    private int menuOpenDx = 100; // 默认 滑动100px 就打开菜单

    // 矫正动画
    private ValueAnimator animator;


    private boolean slideEnable = true;


    public SlideLinearLayout(Context context) {
        this(context, null);
    }

    public SlideLinearLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlideLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlideLinearLayout);
        menuOpenDx = a.getDimensionPixelSize(R.styleable.SlideLinearLayout_sll_menu_open_dx, menuOpenDx);
        a.recycle();

        ViewConfiguration vc = ViewConfiguration.get(context);
        touchScroll = vc.getScaledTouchSlop();
    }

    public void setSlideEnable(boolean slideEnable) {
        this.slideEnable = slideEnable;
    }


    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        contentView = getChildAt(0);
        menuView = getChildAt(1);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 模拟一个线性布局出来
        int contentWidth = contentView.getMeasuredWidth();
        int contentHeight = contentView.getMeasuredHeight();
        contentView.layout(0, 0, contentWidth, contentHeight);

        // 菜单布局，贴在内容布局右边
        int menuWidth = menuView.getMeasuredWidth();
        int menuHeight = menuView.getMeasuredHeight();
        int menuRight = contentWidth + menuWidth;
        menuView.layout(contentWidth, 0, menuRight, menuHeight);

    }

    private final PointF lastPointF = new PointF();
    private final RectF tempRectF = new RectF();
    private int movedDistance = 0;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (slideEnable) {
            log("onInterceptTouchEvent: %s", ev.toString());
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                return true;
            }
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
//        log("onTouchEvent: %s", ev.toString());
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastPointF.set(ev.getX(), ev.getY());

                if (animator != null) {
                    animator.cancel();
                    animator = null;
                }

                movedDistance = 0;
                dispatchTouchEvent2Child(ev);
                return true;
            case MotionEvent.ACTION_MOVE:
                float x = ev.getX();
                float y = ev.getY();
                float dx = x - lastPointF.x;
                float dy = y - lastPointF.y;

                boolean onTouchEvent = false;
                // 平移布局内容
                boolean scroll2right = dx > 0;
                if (move(Math.round(dx))) {
//                    log("onTouchEvent: onTouchEvent = true");
                    onTouchEvent = true;
                    getParent().requestDisallowInterceptTouchEvent(true);
                } else if (scroll2right) {
                    // 手指向右滑动，左侧内容逐渐展示时，等于是关闭菜单的操作，此时灵活处理
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                lastPointF.set(ev.getX(), ev.getY());

                return onTouchEvent;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                getParent().requestDisallowInterceptTouchEvent(false);
                if (movedDistance <= touchScroll) {
                    // 这是个点击事件
                    dispatchTouchEvent2Child(ev);
                }

                // 自动矫正归位
                adjustPos();
                break;
        }
        return true;
    }

    private void dispatchTouchEvent2Child(MotionEvent ev) {
        MotionEvent obtain = MotionEvent.obtain(ev);
        obtain.offsetLocation(getScrollX(), 0);
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            tempRectF.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
            if (tempRectF.contains(obtain.getX(), obtain.getY())) {
                child.dispatchTouchEvent(obtain);
            }
        }
        obtain.recycle();
    }

    @Override
    public boolean canScrollHorizontally(int direction) {
        int menuWidth = menuView.getMeasuredWidth();
        int scrollX = getScrollX();
//        log("canScrollHorizontally direction: %s, scrollX: %s, menuWidth: %s", direction, scrollX, menuWidth);
        if (direction < 0) {
            return 0 < scrollX;
        } else return scrollX < menuWidth;
    }

    private void adjustPos() {
        int scrollX = getScrollX();

        if (scrollX > menuOpenDx) {
            // 展开菜单
            open();
        } else {
            // 收起菜单
            close();
        }
        animator.setDuration(200);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int nextScrollX = (int) animation.getAnimatedValue();
                moveTo(nextScrollX);
            }
        });
        animator.start();
    }

    public void close() {
        int scrollX = getScrollX();
        // 收起菜单
        animator = ValueAnimator.ofInt(scrollX, 0);
        log("close menu scrollX: %d, to: 0", scrollX);

        animator.setDuration(200);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int nextScrollX = (int) animation.getAnimatedValue();
                moveTo(nextScrollX);
            }
        });
        animator.start();
    }

    public void reset() {
        scrollTo(0, 0);
    }

    public void open() {
        int menuWidth = menuView.getMeasuredWidth();
        int scrollX = getScrollX();
        // 展开菜单
        animator = ValueAnimator.ofInt(scrollX, menuWidth);
        log("open menu scrollX: %d, to: %d", scrollX, menuWidth);

        animator.setDuration(200);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int nextScrollX = (int) animation.getAnimatedValue();
                moveTo(nextScrollX);
            }
        });
        animator.start();
    }


    private boolean move(int dx) {
        boolean move = canScrollHorizontally(-dx);
        // 累计滑动距离，避免误触发单击事件
        movedDistance += Math.abs(dx);
        // 判断边界
        int scrollX = getScrollX();
        int nextScrollX = scrollX - dx;
        moveTo(nextScrollX);

        return move;
    }

    private void moveTo(int nextScrollX) {
        int menuWidth = menuView.getMeasuredWidth();
        if (nextScrollX > menuWidth) {
            nextScrollX = menuWidth;
        } else if (nextScrollX < 0) {
            nextScrollX = 0;
        }
        scrollTo(nextScrollX, 0);

//        log("nextScrollX: %d", nextScrollX);
    }


    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        reset();
    }
}
